import { Injectable, BadRequestException } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { DataSource, Repository } from "typeorm";
import { GoldTradeOrderEntity } from "../entities/gold-trade-order.entity";
import { CharacterResourcesEntity } from "../entities/character-resources.entity";
import { SystemMessageService } from "./system-message.service";

interface AggregatedLevel {
  type: 1 | 2; // 1=买 2=卖
  price: number;
  count: number;
  goods_id: number;
  site_id: number;
}

interface TradeNotification {
  receiverId: number;
  content: string;
  time: string;
}

@Injectable()
export class GoldTradeService {
  constructor(
    @InjectRepository(GoldTradeOrderEntity)
    private readonly orderRepo: Repository<GoldTradeOrderEntity>,
    @InjectRepository(CharacterResourcesEntity)
    private readonly resRepo: Repository<CharacterResourcesEntity>,
    private readonly dataSource: DataSource,
    private readonly systemMessageService: SystemMessageService
  ) {}

  private formatDateTime(date: Date): string {
    const pad2 = (n: number) => String(n).padStart(2, "0");
    const y = date.getFullYear();
    const m = pad2(date.getMonth() + 1);
    const d = pad2(date.getDate());
    const hh = pad2(date.getHours());
    const mm = pad2(date.getMinutes());
    const ss = pad2(date.getSeconds());
    return `${y}-${m}-${d} ${hh}:${mm}:${ss}.0`;
  }

  // 聚合买/卖前5档
  async getTopLevels(): Promise<AggregatedLevel[]> {
    // 买盘(求购): 价格降序取前5(顶部买盘), 返回时改为升序展示
    const buyRaw = await this.orderRepo
      .createQueryBuilder("o")
      .select("o.price", "price")
      .addSelect("SUM(o.remaining)", "count")
      .where("o.shop_type = 1 AND o.state IN (0,1) AND o.remaining > 0")
      .groupBy("o.price")
      .orderBy("o.price", "DESC")
      .limit(5)
      .getRawMany();
    const buy = [...buyRaw].sort(
      (a: any, b: any) => Number(a.price) - Number(b.price)
    );
    // 卖盘(出售): 价格升序, 取前5
    const sell = await this.orderRepo
      .createQueryBuilder("o")
      .select("o.price", "price")
      .addSelect("SUM(o.remaining)", "count")
      .where("o.shop_type = 2 AND o.state IN (0,1) AND o.remaining > 0")
      .groupBy("o.price")
      .orderBy("o.price", "ASC")
      .limit(5)
      .getRawMany();
    const res: AggregatedLevel[] = [];
    for (const r of buy)
      res.push({
        type: 1,
        price: Number(r.price),
        count: Number(r.count),
        goods_id: 0,
        site_id: 0,
      });
    for (const r of sell)
      res.push({
        type: 2,
        price: Number(r.price),
        count: Number(r.count),
        goods_id: 0,
        site_id: 0,
      });
    return res;
  }

  // 我的交易记录(简单：按时间倒序)
  async getMyOrders(
    characterId: number,
    pageNum: number = 0,
    pageSize: number = 50
  ) {
    const qb = this.orderRepo
      .createQueryBuilder("o")
      .where("o.characterId = :cid", { cid: characterId })
      .orderBy(
        // 状态优先级：未成交(0) → 部分成交(1) → 撤销(3) → 成交(2)
        "CASE o.state WHEN 0 THEN 0 WHEN 1 THEN 1 WHEN 3 THEN 2 WHEN 2 THEN 3 ELSE 4 END",
        "ASC"
      )
      .addOrderBy("o.id", "DESC")
      .skip(pageNum * pageSize)
      .take(pageSize);

    const [list, total] = await qb.getManyAndCount();
    return list.map((o) => ({
      id: o.id,
      user_id: o.characterId,
      shop_type: o.shop_type,
      price: o.price,
      count: o.count,
      cj_count: o.count - o.remaining,
      state: o.state,
      can_cancel: o.state === 0 || o.state === 1 ? 1 : 0,
      order_id: 0,
      goods_id: 0,
      goods_name: "",
      site_id: 0,
      creation_date: o.created_at ? this.formatDateTime(o.created_at) : "",
    }));
  }

  // 下单 + 渐进撮合（股票式）
  async placeOrder(
    characterId: number,
    type: 1 | 2,
    count: number,
    price: number
  ) {
    if (count <= 0 || price <= 0)
      throw new BadRequestException("价格或数量不合法");
    const notifications: TradeNotification[] = [];
    const result = await this.dataSource.transaction(async (manager) => {
      // 余额/持仓校验 + 冻结
      const res = await manager.findOne(CharacterResourcesEntity, {
        where: { characterId },
      });
      if (!res) throw new BadRequestException("资源不存在");
      if (type === 1) {
        const need = price * count;
        if ((res.silver || 0) < need)
          throw new BadRequestException("您的银两不足，无法下单");
        await manager.update(
          CharacterResourcesEntity,
          { characterId },
          { silver: (res.silver || 0) - need, updated_at: new Date() }
        );
      } else {
        if ((res.gold || 0) < count)
          throw new BadRequestException("您的金币不足，无法下单");
        await manager.update(
          CharacterResourcesEntity,
          { characterId },
          { gold: (res.gold || 0) - count, updated_at: new Date() }
        );
      }

      // 新建订单
      const now = new Date();
      const order = manager.create(GoldTradeOrderEntity, {
        characterId,
        shop_type: type,
        price,
        count,
        remaining: count,
        state: 0,
        frozen_silver: type === 1 ? price * count : 0,
        frozen_gold: type === 2 ? count : 0,
        created_at: now,
        updated_at: now,
      });
      const saved = await manager.save(GoldTradeOrderEntity, order);

      // 渐进撮合
      await this.matchOrder(manager, saved, notifications);

      return { id: saved.id, notifications };
    });
    return result;
  }

  // 撤单：全部成交不可撤；部分成交仅撤回剩余；未成交全部撤回
  async cancelOrder(
    characterId: number,
    type: 1 | 2,
    orderId: number
  ): Promise<void> {
    await this.dataSource.transaction(async (manager) => {
      const order = await manager.findOne(GoldTradeOrderEntity, {
        where: { id: orderId, characterId, shop_type: type },
      });
      if (!order) throw new BadRequestException("订单不存在或无权限");
      if (order.state === 2)
        throw new BadRequestException("订单已完成，无法撤单");
      if (order.state === 3) throw new BadRequestException("订单已撤销");

      const remaining = Number(order.remaining || 0);
      if (remaining <= 0) {
        throw new BadRequestException("无可撤数量");
      }

      // 返还未成交冻结/占用资源
      if (order.shop_type === 1) {
        // 求购：返还剩余按委托价冻结的银两
        const refundSilver = Number(order.price) * remaining;
        if (refundSilver > 0) {
          const r = await manager.findOne(CharacterResourcesEntity, {
            where: { characterId },
          });
          await manager.update(
            CharacterResourcesEntity,
            { characterId },
            { silver: (r?.silver || 0) + refundSilver, updated_at: new Date() }
          );
        }
      } else if (order.shop_type === 2) {
        // 出售：返还剩余冻结金币
        const refundGold = remaining;
        if (refundGold > 0) {
          const r = await manager.findOne(CharacterResourcesEntity, {
            where: { characterId },
          });
          await manager.update(
            CharacterResourcesEntity,
            { characterId },
            { gold: (r?.gold || 0) + refundGold, updated_at: new Date() }
          );
        }
      }

      // 更新订单为撤单完成：保留 remaining（用于在 1456 中正确计算 cj_count）
      await manager.update(
        GoldTradeOrderEntity,
        { id: order.id },
        { state: 3, updated_at: new Date() }
      );
    });
  }

  private async matchOrder(
    manager: any,
    incoming: GoldTradeOrderEntity,
    notifications: TradeNotification[]
  ): Promise<void> {
    // 获取对手盘：买单撮合卖一；卖单撮合买一
    while (incoming.remaining > 0) {
      const isBuy = incoming.shop_type === 1;
      const qb = manager
        .getRepository(GoldTradeOrderEntity)
        .createQueryBuilder("o")
        .where("o.state IN (0,1) AND o.remaining > 0 AND o.shop_type = :t", {
          t: isBuy ? 2 : 1,
        })
        .orderBy("o.price", isBuy ? "ASC" : "DESC")
        .addOrderBy("o.id", "ASC")
        .limit(1);
      const best = await qb.getOne();
      if (!best) break;
      // 价格可成交判断
      if (isBuy && incoming.price < best.price) break;
      if (!isBuy && incoming.price > best.price) break;

      // 成交量
      const qty = Math.min(incoming.remaining, best.remaining);
      // 成交价 = 被动方价格
      const dealPrice = best.price;
      await this.settleTrade(
        manager,
        incoming,
        best,
        qty,
        dealPrice,
        notifications
      );
      // 刷新订单
      incoming = await manager.findOne(GoldTradeOrderEntity, {
        where: { id: incoming.id },
      });
      if (!incoming) break;
    }

    // 更新入单状态
    const newState =
      incoming.remaining <= 0 ? 2 : incoming.remaining < incoming.count ? 1 : 0;
    await manager.update(
      GoldTradeOrderEntity,
      { id: incoming.id },
      { state: newState, updated_at: new Date() }
    );
  }

  private async settleTrade(
    manager: any,
    incoming: GoldTradeOrderEntity,
    resting: GoldTradeOrderEntity,
    qty: number,
    price: number,
    notifications: TradeNotification[]
  ) {
    // 资金与持仓划转（出售侧收3%手续费）
    const buyer =
      incoming.shop_type === 1 ? incoming.characterId : resting.characterId;
    const seller =
      incoming.shop_type === 2 ? incoming.characterId : resting.characterId;

    const buyerRes = await manager.findOne(CharacterResourcesEntity, {
      where: { characterId: buyer },
    });
    const sellerRes = await manager.findOne(CharacterResourcesEntity, {
      where: { characterId: seller },
    });
    const gross = price * qty;
    const fee = Math.floor(gross * 0.03);
    const net = gross - fee;

    // 买方 +qty 金币（冻银已在下单时扣除，此处仅处理对手是卖单的解冻逻辑）
    await manager.update(
      CharacterResourcesEntity,
      { characterId: buyer },
      { gold: (buyerRes?.gold || 0) + qty, updated_at: new Date() }
    );
    // 卖方 +net 银两
    await manager.update(
      CharacterResourcesEntity,
      { characterId: seller },
      { silver: (sellerRes?.silver || 0) + net, updated_at: new Date() }
    );

    // 解冻与补差：
    // - 买方：按 deal 价格成交，若冻结按委托价，差额退回（此处简单：在订单完成时统一返还剩余冻结）
    // - 卖方：冻结金币随 remaining 递减，最终完成时返还剩余冻结

    // 扣减订单剩余
    await manager.update(
      GoldTradeOrderEntity,
      { id: incoming.id },
      {
        remaining: incoming.remaining - qty,
        updated_at: new Date(),
      }
    );
    await manager.update(
      GoldTradeOrderEntity,
      { id: resting.id },
      {
        remaining: resting.remaining - qty,
        updated_at: new Date(),
        state: resting.remaining - qty <= 0 ? 2 : 1,
      }
    );

    // 若 resting 完成，返还其剩余冻结（卖单返还冻结金币、买单返还冻结银两）
    const updatedResting = await manager.findOne(GoldTradeOrderEntity, {
      where: { id: resting.id },
    });
    if (updatedResting && updatedResting.remaining <= 0) {
      if (updatedResting.shop_type === 1 && updatedResting.frozen_silver > 0) {
        // 买单返还按委托价冻结的未用银两差额
        const used = price * updatedResting.count; // 简化：全按成交价估算
        const shouldFreeze = updatedResting.price * updatedResting.count;
        const refund = Math.max(0, shouldFreeze - used);
        if (refund > 0) {
          const r = await manager.findOne(CharacterResourcesEntity, {
            where: { characterId: updatedResting.characterId },
          });
          await manager.update(
            CharacterResourcesEntity,
            { characterId: updatedResting.characterId },
            { silver: (r?.silver || 0) + refund, updated_at: new Date() }
          );
        }
      }
      if (updatedResting.shop_type === 2 && updatedResting.frozen_gold > 0) {
        // 卖单返还未用冻结金币
        const refundGold = Math.max(
          0,
          updatedResting.count -
            (updatedResting.count - updatedResting.remaining)
        );
        if (refundGold > 0) {
          const r = await manager.findOne(CharacterResourcesEntity, {
            where: { characterId: updatedResting.characterId },
          });
          await manager.update(
            CharacterResourcesEntity,
            { characterId: updatedResting.characterId },
            { gold: (r?.gold || 0) + refundGold, updated_at: new Date() }
          );
        }
      }
    }

    // 收集系统消息（在事务外统一落库并推送）
    const hh = String(new Date().getHours()).padStart(2, "0");
    const mm = String(new Date().getMinutes()).padStart(2, "0");
    const ss = String(new Date().getSeconds()).padStart(2, "0");
    const time = `${hh}:${mm}:${ss}`;
    notifications.push({
      receiverId: buyer,
      content: `您订购的金以单价${price}的价格成功买入了${qty}个`,
      time,
    });
    notifications.push({
      receiverId: seller,
      content: `您供货的金以单价${price}的价格成功出售了${qty}个`,
      time,
    });
  }
}
